package controller

import (
	"html/template"
	"net/http"
	"sync"

	"commonUtils/config"
	"commonUtils/web/plugin"

	"commonUtils/utils"
)

type Controller interface {
	Handlers(o Controller) map[string]http.HandlerFunc
	Template() *template.Template
	GetName() string
	SetSpacename(name string)
	GetSpaceName() string
	IsAuthController() bool
	initTemps() error
}

type ControllerManager struct {
	muNamespaces sync.RWMutex
	namespaces   map[string]*Namespace

	indexTamps *template.Template

	captchaManager *utils.CaptchaManager
	smsManager     *utils.VerifyCodeManager
	mailManager    *utils.VerifyCodeManager

	checkAuth func(*Context) (string, int)
	baseCheck func(*Context) (string, int)
}

var inst *ControllerManager

func Manager() *ControllerManager {
	if nil != inst {
		return inst
	}
	inst = &ControllerManager{
		namespaces:     make(map[string]*Namespace),
		captchaManager: utils.NewCaptchaManager(config.Inst().MaxLoginFailCount, &config.Inst().Captcha),
		smsManager:     utils.NewVerifyCodeManager(),
		mailManager:    utils.NewVerifyCodeManager(),
	}

	if len(config.Inst().IndexFiles) > 0 {
		if "" != config.Inst().BaseTempPath {
			files, err := utils.GetFileListByPath(config.Inst().BaseTempPath, true)
			if err != nil {
				utils.Error("base template path parse error : %s", err)
			} else {
				if len(files) > 0 {
					config.Inst().IndexFiles = append(config.Inst().IndexFiles, files...)
				}
			}
		}

		t, err := template.ParseFiles(config.Inst().IndexFiles...)
		if err != nil {
			utils.Error("index template parse error : %s", err)
		} else {
			inst.indexTamps = t
		}
	}

	return inst
}

func (m *ControllerManager) GetNamespace(name string) *Namespace {
	m.muNamespaces.Lock()
	defer m.muNamespaces.Unlock()
	if n, ok := m.namespaces[name]; ok {
		return n
	}
	n := NewNamespace(name)
	m.namespaces[name] = n
	return n

}

func (m *ControllerManager) SetSpaceAuthCheck(name string, f func(*Context) (string, int)) {
	m.GetNamespace(name).CheckAuthCall(f)
}

func (m *ControllerManager) SetSpaceBaseCheck(name string, f func(*Context) (string, int)) {
	m.GetNamespace(name).BaseCheckCall(f)
}

func (m *ControllerManager) Regist(name string, c ...Controller) *ControllerManager {
	n := m.GetNamespace(name)
	n.Regist(c...)
	return m
}

func (m *ControllerManager) CaptchaManager() *utils.CaptchaManager {
	return m.captchaManager
}

func (m *ControllerManager) SmsManager() *utils.VerifyCodeManager {
	return m.smsManager
}

func (m *ControllerManager) MailManager() *utils.VerifyCodeManager {
	return m.mailManager
}

func (m *ControllerManager) CheckAuthCall(f func(*Context) (string, int)) {
	m.checkAuth = f
}

func (m *ControllerManager) SetBaseCheck(f func(*Context) (string, int)) {
	m.baseCheck = f
}

func (m *ControllerManager) CheckAuth(c *Context) (string, int) {
	spaceName := c.C.GetSpaceName()
	space := m.GetNamespace(spaceName)
	if space.checkAuth != nil {
		return space.checkAuth(c)
	}
	if nil == m.checkAuth {
		return "checkAuth function not exist", 50000
	}
	return m.checkAuth(c)
}

func (m *ControllerManager) AllRoutes() map[string]http.Handler {
	m.muNamespaces.RLock()
	defer m.muNamespaces.RUnlock()

	if len(m.namespaces) == 0 {
		return nil
	}

	ret := make(map[string]http.Handler)
	for _, c := range m.namespaces {
		routes := c.AllRoutes()
		if len(routes) > 0 {
			for name, route := range routes {
				if _, ok := ret[name]; ok {
					utils.Warning("Route %s repeat! ignore!", name)
					continue
				}
				ret[name] = route
			}
		}
	}

	if _, ok := ret["/"]; !ok && m.indexTamps != nil {
		ret["/"] = plugin.Manager().InstallHandler(func(w http.ResponseWriter, r *http.Request) {

			path := r.URL.Path
			if "/favicon.ico" == path {
				var favIco = config.Inst().StaticFolder + path
				if utils.IsFile(favIco) {
					http.ServeFile(w, r, favIco)
					return
				} else {
					http.Error(w, "Not found", http.StatusNotFound)
					return
				}
			}

			if m.indexTamps.Lookup("index") != nil {
				m.indexTamps.ExecuteTemplate(w, "index", nil)
			} else {
				m.indexTamps.Execute(w, nil)
			}
		})
	}

	return ret
}

func (m *ControllerManager) GetIndexTamps() *template.Template {
	return m.indexTamps
}
